classdef LinearSupportVectorMachine ...
        < SupportVectorMachine ...
        & BinaryClassClassifier ...
        & Modelable
    %LINEARSUPPORTVECTORMACHINE Summary of this class goes here
    %   Detailed explanation goes here
    
    properties ( SetAccess = protected )
        cost = 1
        isProb = 0
    end
    
    methods
        function [  ] = setCost( this, cost )
            %SETCOST Summary of this function goes here
            %   Detailed explanation goes here
            
            this.cost = cost;
        end
        
        function [  ] = setIsProb( this, isProb )
            %SETISPROB Summary of this function goes here
            %   Detailed explanation goes here
            
            this.isProb = isProb;
        end
    end
    
    methods
        function [ this ] = LinearSupportVectorMachine( name, cost )
            if nargin == 0
                this.setName('lsvm');
            end
            if nargin >= 1
                this.setName(name);
            end
            if nargin >= 2
                this.setCost(cost);
            end
        end
    end
    
    methods
        function [  ] = build( this, X1, Y1 )
            %BUILD Build `@LinearSupportVectorMachine` model
            %
            % LinearSupportVectorMachine::build( this : SupportVectorMachine
            %                                    X1 : double
            %                                    Y1 : double )
            %                           >>     [  ]
            
            this.model = svmtrain(Y1, X1, ...
                ['-s 0 -t 0 -c ', num2str(this.cost), ...
                ' -b ', num2str(this.isProb), this.genWeightOption(Y1), ' -q']);
        end
        
        function [ result ] = apply( this, X2, Y2 )
            %APPLY Apply `@LinearSupportVectorMachine` model
            %
            % LinearSupportVectorMachine::apply( this:LinearSupportVectorMachine
            %                                    X2:double
            %                                    Y2:double
            %                            >>    [ result.Y_hat:double
            %                                    result.Y_out:double ]
            
            [result.Y_hat, ~, result.Y_out] = svmpredict(Y2, X2, this.model, ...
                ['-b ', num2str(this.isProb)]);
            if(this.isProb)
                result.Y_out = result.Y_out(:, 1);
            end
            
            %TODO: The following block of code needs checking
            if isempty(result.Y_out)
                result.Y_out = result.Y_hat;
            end
            
            % Chech whether sign of Y_out is consistent with that of Y_hat
            if ~this.isProb && all(result.Y_hat.*result.Y_out <= 0)
                result.Y_out = -result.Y_out;
            elseif this.isProb && (result.Y_hat(1) == 1 && result.Y_out(1) < 0.5)
                result.Y_out = 1 - result.Y_out;
            end
        end
    end
    
end

